Skip to content

Add store-agnostic projection dead-letter count read to IEventDatabase (#356)#363

Merged
jeremydmiller merged 1 commit into
mainfrom
feature/356-deadletter-count-read
May 23, 2026
Merged

Add store-agnostic projection dead-letter count read to IEventDatabase (#356)#363
jeremydmiller merged 1 commit into
mainfrom
feature/356-deadletter-count-read

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Closes #356.

What changed

Adds a store-agnostic way to read projection/subscription dead-letter counts to JasperFx.Events.IEventDatabase:

Task<long> CountDeadLetterEventsAsync(ShardName shard, CancellationToken token = default);
Task<IReadOnlyList<DeadLetterShardCount>> FetchDeadLetterCountsAsync(CancellationToken token = default);

public record DeadLetterShardCount(string ProjectionName, string ShardKey, long Count);

DeadLetterShardCount is keyed to align with ShardName.Name (ProjectionName) and ShardName.ShardKey (ShardKey), matching how DeadLetterEvent records them. FetchDeadLetterCountsAsync mirrors the "give me every row" shape of the existing AllProjectionProgress.

Default stand-in implementations

Both members ship with default interface implementations that return 0 / an empty list, so:

  • existing IEventDatabase implementers keep compiling with no changes, and
  • consumers (e.g. CritterWatch via GetServices<IEventDatabase>()) get a graceful no-op until a store implements the real read.

Real implementations are tracked in JasperFx/marten#4546 (Marten — DeadLetterEvent is already a stored document) and JasperFx/polecat#146 (Polecat — note its dead-letter storage is currently a no-op, so it needs storage first).

Why

JasperFx.Events 2.0 made SkipApplyErrors = true the default: a failed Apply() is recorded as a DeadLetterEvent and the shard keeps advancing, so accumulating dead-letter rows is the primary "this projection is unhealthy" signal. Until now the abstraction only let you write them.

Tests

EventTests/Daemon/DeadLetterCountDefaultsTests — a bare IEventDatabase that overrides neither new member; the calls exercise the default stand-ins and assert 0 / empty (green on net9.0 + net10.0).

#356)

JasperFx.Events 2.0 made SkipApplyErrors the default, so accumulating
DeadLetterEvent rows is now the primary "projection unhealthy" signal — but
IEventDatabase only let consumers *write* dead letters, forcing store-specific,
reflection-based reads.

Adds two read members to IEventDatabase:
- CountDeadLetterEventsAsync(ShardName, CancellationToken) — per-shard count
- FetchDeadLetterCountsAsync(CancellationToken) — bulk, one DeadLetterShardCount
  (ProjectionName, ShardKey, Count) per shard, mirroring AllProjectionProgress

Both ship with default interface implementations returning 0 / empty as a
stand-in, so existing IEventDatabase implementers keep compiling and behave
gracefully until they override. Marten (JasperFx/marten#4546) and Polecat
(JasperFx/polecat#146) will provide real implementations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit 2a17152 into main May 23, 2026
1 check passed
jeremydmiller added a commit that referenced this pull request May 23, 2026
Ships the source-generator required-member/duplicate-evolver/hint-name fixes
(#359, marten#4542 + marten#4543) and the new store-agnostic dead-letter count
read on IEventDatabase (#363, #356).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add store-agnostic projection dead-letter count read to IEventDatabase (Marten + Polecat)

1 participant